package com.hwl.ucenter.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hwl.commonutils.exception.GuliException;
import com.hwl.commonutils.result.ResultCodeEnum;
import com.hwl.commonutils.utils.HttpUtils;
import com.hwl.commonutils.utils.JwtInfo;
import com.hwl.commonutils.utils.JwtUtils;
import com.hwl.ucenter.pojo.Member;
import com.hwl.ucenter.service.MemberService;
import com.hwl.ucenter.util.UcenterProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 这个服务的端口一定要为8160！！！详情见：https://www.bilibili.com/video/BV1fi4y1x7on?p=476
 * 非常感谢尚硅谷的内网穿透服务，为我们提供了此次学习的机会~
 * <p>
 * 在浏览器中访问URL
 * http://localhost:8160/api/ucenter/wx/login
 * 扫码登录后会重定向到
 * http://localhost:8160/api/ucenter/wx/callback
 *
 * @author long
 * @since 2021-07-18
 */
@Slf4j
@Controller
@RequestMapping("/api/ucenter/wx")
public class WxController {

    @Resource
    private UcenterProperties ucenterProperties;
    @Resource
    private MemberService memberService;

    /**
     * 生成微信扫描二维码，流程为：
     * 用户请求微信登录第三方应用，第三方应用再去请求微信OAuth2授权登录，微信返回登录二维码，
     * 用户扫码确认登录，这时网址会带上授权临时票据code和state，并被重定向回第三方应用，此项目也就是重定向到本地的控制器中
     */
    @GetMapping("login")
    public String getWxCode(HttpSession session) {
        //微信开放平台授权url，%s相当于?占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        String redirectUri = ucenterProperties.getRedirectUri();
        try {
            redirectUri = URLEncoder.encode(redirectUri, "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage());
            throw new GuliException(ResultCodeEnum.URL_ENCODE_ERROR);
        }

        //生成随机state，防止csrf攻击
        String state = UUID.randomUUID().toString();
        //将state存入session中
        session.setAttribute("wx_open_state", state);

        //设置%s里面值
        String url = String.format(
                baseUrl,
                ucenterProperties.getAppId(),
                redirectUri,
                state);

        //重定向，发起请求
        return "redirect:" + url;
    }

    /**
     * 微信的回调方法，获取扫码登录的用户信息
     */
    @GetMapping("callback")
    public String callback(String code, String state, HttpSession session) {
        if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state)) {
            log.error("非法回调请求");
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

        //实际上是从redis中取
        String sessionState = ((String) session.getAttribute("wx_open_state"));
        if (!state.equals(sessionState)) {
            log.error("非法回调请求");
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

        //携带code临时票据和appid以及appsecret请求access_token
        String accessTokenUrl = "https://api.weixin.qq.com";
        String path = "/sns/oauth2/access_token";
        //组装参数：?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

        Map<String, String> accessTokenParam = new HashMap<>();
        accessTokenParam.put("appid", ucenterProperties.getAppId());
        accessTokenParam.put("secret", ucenterProperties.getAppSecret());
        accessTokenParam.put("code", code);
        accessTokenParam.put("grant_type", "authorization_code");

        JSONObject json;
        try {
            HttpResponse response = HttpUtils.doGet(accessTokenUrl, path, new HashMap<>(), accessTokenParam);
            json = HttpUtils.getJson(response);
        } catch (Exception e) {
            log.error("获取access_token失败");
            throw new GuliException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        Object errcode = json.get("errcode");
        if (errcode != null) {
            Double errCode = (Double) errcode;
            String errMsg = (String) json.get("errmsg");
            log.error("获取access_token失败，code：" + errCode + ", message：" + errMsg);
            throw new GuliException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        //解析出结果中的access_token和openid
        String accessToken = (String) json.get("access_token");
        String openid = (String) json.get("openid");

        //在本地数据库中查找当前微信用户的信息
        Member member = memberService.getOne(new QueryWrapper<Member>().eq("openid", openid));
        if (member == null) {
            //当前用户不存在，则携带access_token去微信的资源服务器获取用户个人信息
            String baseUserInfoUrl = "https://api.weixin.qq.com";
            String infoPath = "/sns/userinfo";
            //组装参数：?access_token=ACCESS_TOKEN&openid=OPENID

            Map<String, String> baseUserInfoParam = new HashMap<>();
            baseUserInfoParam.put("access_token", accessToken);
            baseUserInfoParam.put("openid", openid);

            JSONObject infoJson;
            try {
                HttpResponse response = HttpUtils.doGet(baseUserInfoUrl, infoPath, new HashMap<>(), baseUserInfoParam);
                infoJson = HttpUtils.getJson(response);
            } catch (Exception e) {
                log.error("获取用户信息时失败");
                throw new GuliException(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }

            //失败的响应结果
            Object errcodeObj = infoJson.get("errcode");
            if (errcodeObj != null) {
                Double errCode = (Double) errcodeObj;
                String errmsg = (String) infoJson.get("errmsg");
                log.error("获取用户信息时失败，code：" + errCode + ", message：" + errmsg);
                throw new GuliException(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }

            //解析出结果中的用户个人信息
            String nickname = (String) infoJson.get("nickname");
            String avatar = (String) infoJson.get("headimgurl");
            Integer sex = (Integer) infoJson.get("sex"); //1男性，2女性

            //在本地数据库中插入当前微信用户的信息（使用微信账号在本地服务器注册新用户）
            member = new Member()
                    .setOpenid(openid).setNickname(nickname).setAvatar(avatar).setSex(sex);
            log.info(memberService.save(member) ? "扫码用户注册成功！" : "扫码用户注册失败！");
        }

        JwtInfo jwtInfo = new JwtInfo();
        jwtInfo.setId(member.getId()).setAvatar(member.getAvatar()).setNickname(member.getNickname());
        String token = JwtUtils.getJwtToken(jwtInfo, 1800);

        //返回首页面，通过路径传递token令牌
        return "redirect:http://localhost:3000?token=" + token;
    }


}
